<?php
namespace ChannelThrift\Service;

use \Common\Org\PhpRedis;
use \Services\BailService\InvalidException;


class BailCacheService
{
    protected static $redis = false;
    private $exceptionCode = array();

    //某车商保证金概要缓存
    private $dealerKeyPrefix = null;
    //车商保证金变更队列
    private $bailChangeQueueKey = null;
    //某拍单冻结车商列表
    private $freezeDealerMapKey = null;
    //某车商保证金缓存变更锁
    private $DealerBailLockKey = null;

    public function __construct()
    {
        if (!self::$redis) {
            self::$redis=new PhpRedis(0);
        }
        $this->exceptionCode = C('EXCEPTION_CODE') +  C('BAIL_EXCEPTION_CODE');

        $this->dealerKeyPrefix = getRedisKeyByCode('REDIS_KEY','100056');
        $this->bailChangeQueueKey = getRedisKeyByCode('REDIS_KEY','100057');
        $this->freezeDealerMapKey = getRedisKeyByCode('REDIS_KEY','100058');
        $this->DealerBailLockKey = getRedisKeyByCode('REDIS_KEY','100059');
    }

    /*
    初始化车商保证金到Redis
    @param dealerID  车商ID
    @return void
    */
    private function initDealerBail($dealerID)
    {   
        $dealerkey=$this->dealerKeyPrefix.$dealerID;
        $existsKey = self::$redis->exists($dealerkey);
        if ($existsKey) {
            $dealer = self::$redis->get($dealerkey);    
        }

        if (!$existsKey || !$dealer)
        {
            $dealerObj = new CarDealerService();
            $dealer = $dealerObj->getCarDealer($dealerID);

            //\Think\Log::write('xxxxxx'.var_export($dealerID,true))     ;
            if($dealer){
                //只缓存保证金摘要信息
                $dSummary = array();
                $dSummary['dealer_id'] = intval($dealer['dealer_id']);
                $dSummary['bail_amount'] = floatval($dealer['bail_amount']);
                $dSummary['freeze_amount'] = floatval($dealer['freeze_amount']);
                $dSummary['indemnity_amount'] = floatval($dealer['indemnity_amount']);
                $dSummary['contact_person'] = $dealer['contact_person'];                
                $dSummary['minus_amount'] = floatval($dealer['minus_amount']);                
                $ok = $this->cachingDealerSummary($dealerID,$dSummary);
            }else{
                throw new InvalidException(array('code' => 10001, 'message' => $this->exceptionCode[10001]));
            }
        }
    }

    //缓存数据到redis
    private function cachingDealerSummary($dealerID,$dSummary)
    {
        $dealerkey=$this->dealerKeyPrefix.$dealerID;
        $ok = self::$redis->set($dealerkey,json_encode($dSummary,true),86400);
        return $ok;
    }

    // /*
    // 清空redis中的车商信息
    // @param dealerID 车商ID
    // @param force 是否强制清空，默认为否(即：保证金处理队列bailChangeQueueKey不为空不可清空)，
    // @return true/false
    // */
    // public function cleanDealerBail($dealerID)
    // {
    //    $dealerkey=$this->dealerKeyPrefix.$dealerID;
    //    return self::$redis->del($dealerkey);

    // }

    /*
    获得车商保证金概况
    @param $dealerID  车商ID
    @return array/null 车商保证金信息概况
    */
    public function getDealerBail($dealerID)
    {
        //$this->cleanDealerBail($dealerID);  //单元测试用
        $this->initDealerBail($dealerID);
        $dealerkey=$this->dealerKeyPrefix.$dealerID;        
        $dealer = self::$redis->get($dealerkey);
  
        if ($dealer){
            return json_decode($dealer,true);
        } else {
            return null;
        }
    }

    /*
    保证金变更持久化入队[增加，扣除，违约扣除，冻结，解冻]
    @param $use_type 保证金应用类型 1充值 2冻结出价保证金 3解冻出价保证金 4提取 5争议支出 6违约赔付 7其它 8消费
    @param $info array 
        `dealer_id` '车商id',            
        `order_id` '拍单号',
        `use_time` '保证金使用时间',
        //`use_type` '保证金应用类型 1充值 2冻结出价保证金 3解冻出价保证金 4提取 5争议支出 6违约赔付 7其它  8消费',
        `use_detail` '应用明细',
        `occur_money` '发生金额',
        `remain_amount` '保证金余额',
        `recharge_method` '充值方式1支付宝 2拉卡拉 3线下充值 4网银 5代金券',
        `recharge_no` '充值单号',
        `createtime` '创建时间',
        `free_bl_id` '解冻记录id',
        `is_free` '若为冻结记录时，标识是否已解冻 ，1解冻，0未解冻_',
        `recharge_id` ' 订单表ID',
        `collection_id` '收款单ID',
        `payment_id` '付款单ID'
    @return true/false  
    */
    public function bailChangePushQueue($use_type,$info)
    {
        //多进程改变同一车商保证金，操作互斥,延时操作 
        if (!self::$redis->lock_wait($this->DealerBailLockKey.$info['dealer_id'],500)){
            throw new InvalidException(array('code' => 500059, 'message' => $this->exceptionCode[500059]));
        }

        try{
            $typs = array(1,2,3,4,5,6,8);
            if (!in_array($info['use_type'],$typs)) {
                throw new InvalidException(array('code' => 500057, 'message' => $this->exceptionCode[500057]));
            }
            $typs = array(1,2,3,4,5,6,8);
            if (!in_array($use_type,$typs)) {
                throw new InvalidException(array('code' => 500057, 'message' => $this->exceptionCode[500057]));
            }

            $listKey = $this->bailChangeQueueKey;
            $dealerID = $info['dealer_id'];
            $dSummary = $this->getDealerBail($dealerID); 

            switch ($use_type) {
                case 1:
                    $info['use_type'] = 1;//充值 
                    $bailLog = $this->genRechargeBailLog($dSummary,$info);
                    break;
                case 2:                
                    $info['use_type'] = 2;//冻结
                    $bailLog = $this->genFreezeBailLog($dSummary,$info);
                    break;
                case 3:
                    $info['use_type'] = 3;//解冻
                    $bailLog = $this->genUnFreezeBailLog($dSummary,$info);
                    break;
                case 4:
                    $info['use_type'] = 4;//提取
                    $bailLog = $this->genDecBailLog($dSummary,$info);
                    break;
                case 5:
                    $info['use_type'] = 5;//争议支出 
                    $bailLog = $this->genDecBailLog($dSummary,$info);
                    break;
                case 6:
                    $info['use_type'] = 6;//违约增值
                    $bailLog = $this->genDecBailLog($dSummary,$info);
                    break;
                case 7:
                    //系统手工处理
                    break;
                case 8:
                    $info['use_type'] = 8;//消费
                    $bailLog = $this->genDecBailLog($dSummary,$info);
                    break;
            }

            if (empty($bailLog['use_detail'])) {                
                throw new InvalidException(array('code' => 500058, 'message' => $this->exceptionCode[500058]));
            }

            //入队               
            $appendLen = self::$redis->lPush($listKey,json_encode(array('summary'=>$dSummary,'log'=>$bailLog),true));        
            $ok = $this->cachingDealerSummary($dealerID,$dSummary);

            self::$redis->unlock($this->DealerBailLockKey.$dealerID);
        }catch(\Services\BailService\InvalidException $e){
            self::$redis->unlock($this->DealerBailLockKey.$dealerID);
            throw $e;
        }
        
        return $appendLen;        
    }

    //生成充值记录
    private function genRechargeBailLog(&$dSummary,&$info) 
    {
        if (!is_numeric($info['occur_money']) || $info['occur_money']<1) {
            throw new InvalidException(array('code' => 500056, 'message' => $this->exceptionCode[500056]));
        }
        

        $dSummary['bail_amount'] =  $dSummary['bail_amount']+$info['occur_money'];        

        $bailLog = array();
        $bailLog['dealer_id'] = intval($info['dealer_id']);
        $bailLog['order_id'] = 0;                
        $bailLog['use_time'] =  date('Y-m-d G:i:s',time());
        $bailLog['use_type'] = 1;               
        $bailLog['use_detail'] =  $info['use_detail']?$info['use_detail']:'保证金充值';
        $bailLog['occur_money'] =  floatval($info['occur_money']);
        $bailLog['remain_amount'] =  $dSummary['bail_amount'];
        $bailLog['recharge_method'] =  $info['recharge_method'];
        $bailLog['recharge_no'] =  (string)$info['recharge_no'];
        $bailLog['createtime'] =  '';//todo 此值由消费者程序填入
        $bailLog['free_bl_id'] =  0;
        $bailLog['is_free'] =  0;
        $bailLog['recharge_id'] =  $info['recharge_id'];
        $bailLog['collection_id'] =  $info['collection_id'];                
        $bailLog['payment_id'] =  0;
        return $bailLog;
    }

    //生成冻结记录
    private function genFreezeBailLog(&$dSummary,&$info) 
    {
        
        if (!is_numeric($info['occur_money']) || $info['occur_money']<1) {
            throw new InvalidException(array('code' => 500056, 'message' => $this->exceptionCode[500056]));
        }
        if (intval($info['order_id'])<1) {
            throw new InvalidException(array('code' => 700002, 'message' => $this->exceptionCode[700002]));
        }
        if ($dSummary['bail_amount'] < $info['occur_money']) {
            /* 保证金不足 */            
            throw new InvalidException(array('code' => 10006, 'message' => $this->exceptionCode[10006]));
        }
        $exists = self::$redis->hExists($this->freezeDealerMapKey.$info['order_id'],$info['dealer_id']);
        if ($exists){
            //已冻结车商保证金
            throw new InvalidException(array('code' => 10005, 'message' => $this->exceptionCode[10005]));
        }

        //冻结                
        $dSummary['bail_amount'] =  $dSummary['bail_amount']-$info['occur_money'];
        $dSummary['freeze_amount'] =  $dSummary['freeze_amount']+$info['occur_money'];
        //记录
        $bailLog = array();
        $bailLog['dealer_id'] = intval($info['dealer_id']);
        $bailLog['order_id'] = intval($info['order_id']);
        $bailLog['use_time'] =  date('Y-m-d G:i:s',time());
        $bailLog['use_type'] = 2;               
        $bailLog['use_detail'] =  '冻结保证金,拍单:'.$info['order_id'];
        $bailLog['occur_money'] =  floatval($info['occur_money']);
        $bailLog['remain_amount'] =  $dSummary['bail_amount'];
        $bailLog['recharge_method'] =  0;
        $bailLog['recharge_no'] =  '0';
        $bailLog['createtime'] =  '';//todo 此值由消费者程序填入
        $bailLog['free_bl_id'] =  0;
        $bailLog['is_free'] =  0;
        $bailLog['recharge_id'] =  0;
        $bailLog['collection_id'] =  0;                
        $bailLog['payment_id'] =  0;

        self::$redis->hSet($this->freezeDealerMapKey.$bailLog['order_id'],$info['dealer_id'],json_encode($bailLog,true));

        return $bailLog;
    }

    //获得拍单某车商保证金冻结情况
    public function getFreezeLogInfo($orderID,$dealerID){
        $freezeLog = self::$redis->hGet($this->freezeDealerMapKey.$orderID,$dealerID);
        if (!$freezeLog) {
            $freezeLog = D('DealerBailLog', 'Service')->getDealerBailLog($dealerID, $orderID, 2, 0);
        } else {
            $freezeLog = json_decode($freezeLog,true);
        }
        return $freezeLog;
    }

    //生成解冻记录
    private function genUnFreezeBailLog(&$dSummary,&$info) 
    {        
        $exists = self::$redis->hExists($this->freezeDealerMapKey.$info['order_id'],$info['dealer_id']);
        if (!$exists){
            $bailLog = D('DealerBailLog', 'Service')->getDealerBailLog($info['dealer_id'], $info['order_id'], 2, 0);
            if (empty($bailLog)) {
                //未发现保证金冻结记录
                throw new InvalidException(array('code' => 500053, 'message' => $this->exceptionCode[500053]));
            }
            self::$redis->hSet($this->freezeDealerMapKey.$info['order_id'],$info['dealer_id'],json_encode($bailLog,true));
        }
        $freezeLog = self::$redis->hGet($this->freezeDealerMapKey.$info['order_id'],$info['dealer_id']);
        $freezeLog = json_decode($freezeLog,true); 

        if (!isset($info['occur_money'])){
            //若未指定解冻金额，默认解冻冻结时的金额
            $info['occur_money'] = $freezeLog['occur_money'];
        }elseif (floatval($freezeLog['occur_money'])<floatval($info['occur_money'])) {            
            //若指定了解冻金额，则检查是否 <= 冻结金额
            //冻结保证金不足解冻
            throw new InvalidException(array('code' => 10010, 'message' => $this->exceptionCode[10010]));   
        }
                        
        if ($dSummary['freeze_amount']<$info['occur_money']) {
            //冻结保证金不足解冻
            throw new InvalidException(array('code' => 10010, 'message' => $this->exceptionCode[10010]));   
        }

        // if (!is_numeric($info['occur_money']) || $info['occur_money']<1) {
        //     throw new InvalidException(array('code' => 500056, 'message' => $this->exceptionCode[500056]));
        // }

        if (intval($info['order_id'])<1) {
            throw new InvalidException(array('code' => 700002, 'message' => $this->exceptionCode[700002]));
        }
        if (empty($info['use_detail'])) {            
            $use_detail = '解冻保证金,拍单:'.$info['order_id'];
        }else{
             $use_detail = $info['use_detail'];
        }
        
        //解冻                
        $dSummary['bail_amount'] =  $dSummary['bail_amount']+$info['occur_money'];
        $dSummary['freeze_amount'] =  $dSummary['freeze_amount']-$info['occur_money'];
        //记录
        $bailLog = array();
        $bailLog['dealer_id'] = intval($info['dealer_id']);
        $bailLog['order_id'] = intval($info['order_id']);
        $bailLog['use_time'] =  date('Y-m-d G:i:s',time());
        $bailLog['use_type'] = 3;               
        $bailLog['use_detail'] =  $use_detail;
        $bailLog['occur_money'] =  floatval($info['occur_money']);
        $bailLog['remain_amount'] =  $dSummary['bail_amount'];
        $bailLog['recharge_method'] =  0;
        $bailLog['recharge_no'] =  '0';
        $bailLog['createtime'] =  '';//todo 此值由消费者程序填入
        $bailLog['free_bl_id'] =  0;//todo 此值由消费者程序更新填入 ,以及冻结记录的is_free=1
        $bailLog['is_free'] =  0;   
        $bailLog['recharge_id'] =  0;
        $bailLog['collection_id'] =  0;                
        $bailLog['payment_id'] =  0;

        self::$redis->hDel($this->freezeDealerMapKey.$bailLog['order_id'],$info['dealer_id']);



        return $bailLog;
    }

    //生成减少记录
    private function genDecBailLog(&$dSummary,&$info) 
    {        

        $typs = array(4,5,6,8);
        if(!in_array($info['use_type'],$typs)){            
            throw new InvalidException(array('code' => 500057, 'message' => $this->exceptionCode[500057]));
        }

        if (in_array(intval($info['use_type']),array(4,8))) {
            $order_id = 0;
        } else {
            if (intval($info['order_id'])<1){
                // \Think\Log::write(var_export($dSummary,true),'WARN'); 
                throw new InvalidException(array('code' => 700002, 'message' => $this->exceptionCode[700002]));
            }else{
                $order_id = $info['order_id'];
            }
        }
        //\Think\Log::write(var_export($info,true));        
        if (!is_numeric($info['occur_money'])) {
            throw new InvalidException(array('code' => 500056, 'message' => $this->exceptionCode[500056]));
        } elseif (in_array($info['use_type'] ,array(4,8))){
            //从可用中提取
            if (floatval($dSummary['bail_amount']) < floatval($info['occur_money'])) {
                /*可用余额不足提取或消费 */                                
                throw new InvalidException(array('code' => 10006, 'message' => $this->exceptionCode[10006]));
            }
            $info['remain_amount'] = $dSummary['bail_amount'] =  $dSummary['bail_amount']-$info['occur_money']; 
        } else {
            //从冻结中扣除
            if (floatval($dSummary['freeze_amount']) < floatval($info['occur_money'])) {
                /*冻结余额余额不足扣除 */             
                throw new InvalidException(array('code' => 10019, 'message' => $this->exceptionCode[10019]));
            }
            $info['remain_amount'] = $dSummary['bail_amount'];
            $dSummary['freeze_amount'] =  $dSummary['freeze_amount']-$info['occur_money'];
            $dSummary['indemnity_amount'] =  $dSummary['indemnity_amount']+$info['occur_money'];
        }
        
        //记录
        $bailLog = array();
        $bailLog['dealer_id'] = intval($info['dealer_id']);
        $bailLog['order_id'] = intval($order_id);
        $bailLog['use_time'] =  date('Y-m-d G:i:s',time());
        $bailLog['use_type'] = $info['use_type'];
        $bailLog['use_detail'] =  $info['use_detail'];
        $bailLog['occur_money'] =  floatval($info['occur_money']);
        $bailLog['remain_amount'] =  $info['remain_amount'];
        $bailLog['recharge_method'] =  $info['recharge_method'];
        $bailLog['recharge_no'] =  '0';
        $bailLog['createtime'] =  '';//todo 此值由消费者程序填入
        $bailLog['free_bl_id'] =  0; //todo use_type 为5或6时，此值由消费者程序填入冻结记录bl_id
        $bailLog['is_free'] =  0;
        $bailLog['recharge_id'] =  0;
        $bailLog['collection_id'] =  $info['collection_id'];                
        $bailLog['payment_id'] =   $info['payment_id'] ;
        return $bailLog;
    }

}
